package com.java.jvalidator.framework.validator;

import java.lang.reflect.Array;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Collection;
import java.util.Date;

import com.java.jvalidator.framework.TypeUtil;
import com.java.jvalidator.framework.ValidationException;
import com.java.jvalidator.framework.ValidationExceptionMsg;
import com.java.jvalidator.framework.config.Parameters;

public class RangeValidator extends AbstractValidator {
	private static Validator instance = new RangeValidator();

	public static Validator getInstance() {
		return instance;
	}

	protected boolean beginValidate(Object target, String propertyName, Parameters param) throws ValidationException {
		Object fieldValue = TypeUtil.getPropertyValue(target, propertyName);
		if (fieldValue == null) {
			return true;
		}

		if ((!hasMin(param)) && (!hasMax(param))) {
			throw new ValidationException("Error occurs,the validation parameter is invalid!");
		}

		if ((fieldValue instanceof String)) {
			if (TypeUtil.isStringNullOrEmpty((String) fieldValue)) {
				return true;
			}
			Double value = Double.valueOf(((String) fieldValue).length());
			return validateData(value, param);
		}
		if (fieldValue.getClass().isArray()) {
			Double value = Double.valueOf(Array.getLength(fieldValue));
			return validateData(value, param);
		}
		if ((fieldValue instanceof Collection)) {
			Double value = Double.valueOf(((Collection<?>) fieldValue).size());
			return validateData(value, param);
		}
		if (TypeUtil.isDigital(fieldValue)) {
			Double value = Double.valueOf(TypeUtil.convertNumberToDouble(fieldValue));
			return validateData(value, param);
		}
		if ((fieldValue instanceof Date)) {
			return validateDate((Date) fieldValue, param);
		}
		throw new ValidationException(ValidationExceptionMsg.VALIDATION_ERROR);
	}

	private boolean validateData(Double value, Parameters param) throws ValidationException {
		if (!checkMinMaxAsNumber(param))
			throw new ValidationException("The min value shouldn't be greater than max");
		try {
			if (hasMax(param)) {
				Double max = Double.valueOf(Double.parseDouble(param.getParameterValueAsString("max")));
				if (value.doubleValue() > max.doubleValue()) {
					return false;
				}
			}
			if (hasMin(param)) {
				Double min = Double.valueOf(Double.parseDouble(param.getParameterValueAsString("min")));
				if (value.doubleValue() < min.doubleValue())
					return false;
			}
		} catch (NumberFormatException e) {
			throw new ValidationException(e);
		}
		return true;
	}

	private boolean checkMinMaxAsDate(Parameters param) throws ValidationException {
		if ((hasMax(param)) && (hasMin(param))) {
			String dateFormat = param.getParameterValueAsString("dateFormat");
			SimpleDateFormat formatter = null;
			if (TypeUtil.isStringNullOrEmpty(dateFormat))
				formatter = new SimpleDateFormat();
			else
				formatter = new SimpleDateFormat(dateFormat);
			try {
				Date max = formatter.parse(param.getParameterValueAsString("max"));
				Date min = formatter.parse(param.getParameterValueAsString("min"));
				return (min.before(max)) || (min.equals(max));
			} catch (ParseException e) {
				throw new ValidationException(e);
			}
		}
		return true;
	}

	private boolean checkMinMaxAsNumber(Parameters param) throws ValidationException {
		if ((hasMax(param)) && (hasMin(param))) {
			try {
				Double max = Double.valueOf(param.getParameterValueAsString("max"));
				Double min = Double.valueOf(param.getParameterValueAsString("min"));
				return min.doubleValue() <= max.doubleValue();
			} catch (NumberFormatException e) {
				throw new ValidationException(e);
			}
		}
		return true;
	}

	private boolean validateDate(Date fieldValue, Parameters param) throws ValidationException {
		if (!checkMinMaxAsDate(param)) {
			throw new ValidationException("Error occurs,the validation parameter is invalid!");
		}
		String dateFormat = param.getParameterValueAsString("dateFormat");
		SimpleDateFormat formatter = null;
		if (TypeUtil.isStringNullOrEmpty(dateFormat))
			formatter = new SimpleDateFormat();
		else {
			try {
				formatter = new SimpleDateFormat(dateFormat);
			} catch (IllegalArgumentException e) {
				throw new ValidationException(e);
			}
		}
		if (hasMax(param)) {
			try {
				Date maxDate = formatter.parse(param.getParameterValueAsString("max"));
				if (fieldValue.after(maxDate))
					return false;
			} catch (ParseException e) {
				throw new ValidationException(e);
			}
		}
		if (hasMin(param)) {
			try {
				Date minDate = formatter.parse(param.getParameterValueAsString("min"));
				if (fieldValue.before(minDate))
					return false;
			} catch (ParseException e) {
				throw new ValidationException(e);
			}
		}
		return true;
	}

	private boolean hasMin(Parameters param) {
		if (param == null) {
			return false;
		}
		return !TypeUtil.isStringNullOrEmpty(param.getParameterValueAsString("min"));
	}

	private boolean hasMax(Parameters param) {
		if (param == null) {
			return false;
		}
		return !TypeUtil.isStringNullOrEmpty(param.getParameterValueAsString("max"));
	}
}
